package cn.xzc.job.cocurrent;

import cn.xzc.job.util.ThrowableUtil;

import java.util.ArrayDeque;
import java.util.Queue;

/**
 * 维护一系列异步响应通知信息集合【成功、失败】
 *
 * @author xzc
 * 当前时间 2024-02-12 21:30:00
 */
public class ListenableFutureCallbackRegistry<T> {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ListenableFutureCallbackRegistry.class);

    private final Queue<SuccessCallback<? super T>> successCallbacks = new ArrayDeque<>(1);

    private final Queue<FailureCallback> failureCallbacks = new ArrayDeque<>(1);
    private final Object mutex = new Object();
    private State state = State.NEW;
    
    private Object result;

    /**
     * Add the given callback to this registry.
     * 将本任务的所有成功和失败回调通知保存本队列中
     *
     * @param callback the callback to add
     */
    public void addCallback(ListenableFutureCallback<? super T> callback) {
        synchronized (this.mutex) {
            switch (this.state) {
                case NEW:
                    this.successCallbacks.add(callback);
                    this.failureCallbacks.add(callback);
                    break;
                case SUCCESS:
                    notifySuccess(callback);
                    break;
                case FAILURE:
                    notifyFailure(callback);
                    break;
                default:
                    throw new IllegalStateException("不存在这种枚举值处理方式！");
            }
        }
    }

    /**
     * 通知所有成功回调
     *
     * @param callback 回调通知
     */
    @SuppressWarnings("unchecked")
    private void notifySuccess(SuccessCallback<? super T> callback) {
        try {
            callback.onSuccess((T) this.result);
        } catch (Throwable ex) {
            // Ignore
            log.error("保存所有成功通知异常失败：\n{}", ThrowableUtil.getThrowableMessage(ex));
        }
    }

    /**
     * 通知所有失败回调
     *
     * @param callback 回调通知
     */
    private void notifyFailure(FailureCallback callback) {
        try {
            callback.onFailure((Throwable) this.result);
        } catch (Throwable ex) {
            // Ignore
            log.error("保存所有失败通知异常失败：\n{}", ThrowableUtil.getThrowableMessage(ex));
        }
    }

    /**
     * Add the given success callback to this registry.
     * 保存本任务的所有成功回调通知
     *
     * @param callback the success callback to add
     */
    public void addSuccessCallback(SuccessCallback<? super T> callback) {
        synchronized (this.mutex) {
            switch (this.state) {
                case NEW:
                    this.successCallbacks.add(callback);
                    break;
                case SUCCESS:
                    notifySuccess(callback);
                    break;
                default:
                    throw new IllegalStateException("不存在这种枚举值处理方式！");
            }
        }
    }

    /**
     * Add the given failure callback to this registry.
     * 保存本任务的所有失败回调通知
     *
     * @param callback the failure callback to add
     */
    public void addFailureCallback(FailureCallback callback) {
        synchronized (this.mutex) {
            switch (this.state) {
                case NEW:
                    this.failureCallbacks.add(callback);
                    break;
                case FAILURE:
                    notifyFailure(callback);
                    break;
                default:
                    throw new IllegalStateException("不存在这种枚举值处理方式！");
            }
        }
    }

    /**
     * Trigger a {@link ListenableFutureCallback#onSuccess(Object)} call on all added callbacks with the given result.
     * 回调通知给所有对应任务的成功通知信息
     *
     * @param result the result to trigger the callbacks with
     */
    public void success(
                        T result) {
        synchronized (this.mutex) {
            this.state = State.SUCCESS;
            this.result = result;
            SuccessCallback<? super T> callback;
            while ((callback = this.successCallbacks.poll()) != null) {
                notifySuccess(callback);
            }
        }
    }

    /**
     * Trigger a {@link ListenableFutureCallback#onFailure(Throwable)} call on all added callbacks with the given {@code Throwable}.
     * 回调通知给所有对应任务的失败通知信息
     *
     * @param ex the exception to trigger the callbacks with
     */
    public void failure(Throwable ex) {
        synchronized (this.mutex) {
            this.state = State.FAILURE;
            this.result = ex;
            FailureCallback callback;
            while ((callback = this.failureCallbacks.poll()) != null) {
                notifyFailure(callback);
            }
        }
    }


    private enum State {NEW, SUCCESS, FAILURE}

}
